#include "stdafx.h"
#include "XImageDrawImpl.h"

#include "Util.h"
#include "DrawUtil.h"
#include "XCodeDefine.h"
#include "IntSkinImpl.h"

#include "IXImageMgr.h"
#include "IXIconMgr.h"

IMPLEMENT_SKIN_CLASS(CXImageDrawImpl);


#define BUFFER_IMAGE_STUB_HANDLE    ((HANDLE)0xF2F4F6F8)

CXImageDrawImpl::CXImageDrawImpl()
{
    m_xImage = NULL;
    m_xColorizeImage = NULL;

    memset(&m_Size, 0, sizeof(m_Size));

    m_nHorzIndex = 0;
    m_nVertIndex = 0;

    m_nHorzCount = 1;
    m_nVertCount = 1;

    m_nAlphaMode  = Alpha_DefinedAlpha;
    m_byAlphaValue    = 0;
    m_clrTrans   = RGB(255, 0, 255);

    m_dwFlags = 0;

    m_DrawType = IDT_Plain;
}

CXImageDrawImpl::~CXImageDrawImpl()
{
    ;
}


//////////////////////////////////////////////////////////////////////////
// IXImageDraw

HRESULT CXImageDrawImpl::put_ImagePath(IN LPCTSTR szXNC)
{
    if(_tcsicmp(m_strXNC.GetData(), szXNC) == 0)
        return S_OK;

    ReleaseData();

    HRESULT hResult = S_OK;
    XPtr<IXImageMgr> pImageMgr = NULL;

    m_strXNC = szXNC;
    m_dwFlags = IntSkinImpl::ChangeFlags(m_dwFlags, ImageFlag_Image, ImageFlag_Icon);

    hResult = Util::CreateObject(&pImageMgr);
    CHECK_HRESULT(hResult);

    XASSERT_WARNING(_tcsstr(szXNC, _T("\\\\")) == NULL, _T("XNC Doesn't support double slash"));

    hResult = pImageMgr->LoadImage(szXNC, &m_xImage);
    XASSERT_ERROR(SUCCEEDED(hResult), _T("ImageLoad Failed"));
    CHECK_HRESULT(hResult);

    if((m_nAlphaMode == Alpha_DefinedAlpha || m_nAlphaMode == Alpha_TransAlpha)
        && DrawUtil::GetImageBits(m_xImage) < 32)
        m_nAlphaMode = Alpha_BitBlt;

    if(DrawUtil::GetXImageSize(m_xImage, &m_Size))
        return S_OK;
    return E_FAIL;
}

HRESULT CXImageDrawImpl::put_Icon(IN LPCTSTR szXNC, BOOL bBig)
{
    if((_tcsicmp(m_strXNC.GetData(), szXNC) == 0) && ((!!bBig) == IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_BigIcon)))
        return S_OK;

    ReleaseData();

    HRESULT hResult = S_OK;
    XPtr<IXIconMgr> pIconMgr = NULL;

    m_strXNC = szXNC;
    m_dwFlags = IntSkinImpl::ChangeFlags(m_dwFlags, ImageFlag_Icon, ImageFlag_Image);

    hResult = Util::CreateObject(&pIconMgr);
    CHECK_HRESULT(hResult);

    hResult = pIconMgr->LoadIcon(szXNC, bBig, (XICON*)&m_xImage);
    CHECK_HRESULT(hResult);

    m_dwFlags = IntSkinImpl::ModifyFlags(m_dwFlags, ImageFlag_BigIcon, bBig);

    if(DrawUtil::GetXIconSize((XICON)m_xImage, &m_Size))
        return S_OK;

    return E_FAIL;
}

HRESULT CXImageDrawImpl::put_Icon(IN XICON xIcon, IN BOOL bDestroy)
{
    if(m_xImage == (XIMAGE)xIcon)
        return S_OK;

    ReleaseData();

    m_xImage = (XIMAGE)xIcon;

    m_strXNC = _T("");
    m_dwFlags = IntSkinImpl::ChangeFlags(m_dwFlags, ImageFlag_Icon, ImageFlag_Image);
    if(!bDestroy)
        m_dwFlags |= ImageFlag_NotDelete;

    m_nHorzCount = 1;
    m_nVertCount = 1;

    if(DrawUtil::GetXIconSize((XICON)m_xImage, &m_Size))
        return S_OK;

    return E_FAIL;
}

HRESULT CXImageDrawImpl::get_ImagePath(OUT BSTR* pszXNC)
{
    if(::SysReAllocString(pszXNC, m_strXNC.GetData()))
        return S_OK;
    return E_FAIL;
}

HRESULT CXImageDrawImpl::put_Image(IN XIMAGE xImage)
{
    if(xImage == m_xImage)
        return S_OK;

    ReleaseData();

    HRESULT hResult = S_OK;

    m_strXNC = _T("");
    m_dwFlags = IntSkinImpl::ChangeFlags(m_dwFlags, ImageFlag_Image, ImageFlag_Icon);
    m_xImage = xImage;

    if((m_nAlphaMode == Alpha_DefinedAlpha || m_nAlphaMode == Alpha_TransAlpha)
        && DrawUtil::GetImageBits(m_xImage) < 32)
        m_nAlphaMode = Alpha_BitBlt;

    if(DrawUtil::GetXImageSize(m_xImage, &m_Size))
        return S_OK;
    return E_FAIL;
}

void CXImageDrawImpl::put_DrawType(IN ImageDrawType type)
{
    m_DrawType = type;
}

HRESULT CXImageDrawImpl::put_DrawParams(IN ImageDrawParams* pParam)
{
    m_DrawType = (pParam == NULL) ? IDT_Plain : ((m_DrawType == IDT_Plain) ? IDT_JiuGong : m_DrawType);

    if(pParam != NULL)
        memcpy(&m_DrawParams, pParam, sizeof(m_DrawParams));
    return S_OK;
}

HRESULT CXImageDrawImpl::get_DrawParams(OUT ImageDrawParams* pParam)
{
    if(m_DrawType == IDT_Plain)
        memset(pParam, 0, sizeof(ImageDrawParams));
    else
        memcpy(pParam, &m_DrawParams, sizeof(ImageDrawParams));
    return S_OK;
}

HRESULT CXImageDrawImpl::get_ImageSize(OUT LPSIZE lpSize)
{
    memcpy(lpSize, &m_Size, sizeof(m_Size));
    if(m_nHorzCount != 0)
        lpSize->cx /= m_nHorzCount;
    if(m_nVertCount != 0)
        lpSize->cy /= m_nVertCount;
    return S_OK;
}

HRESULT CXImageDrawImpl::put_Index(IN INT nHIndex, IN INT nVIndex)
{
    m_nHorzIndex = (nHIndex < m_nHorzCount) ? nHIndex : 0;
    m_nVertIndex = (nVIndex < m_nVertCount) ? nVIndex : 0;
    return S_OK;
}

HRESULT CXImageDrawImpl::get_Index(IN LPINT pnHIndex, IN LPINT pnVIndex)
{
    *pnHIndex = m_nHorzIndex;
    *pnVIndex = m_nVertIndex;
    return S_OK;
}

HRESULT CXImageDrawImpl::put_SubCount(IN INT nHCount, IN INT nVCount)
{
    m_nHorzCount = (nHCount <= 0) ? 1 : nHCount;
    m_nVertCount = (nVCount <= 0) ? 1 : nVCount;;
    return S_OK;
}

HRESULT CXImageDrawImpl::get_SubCount(IN LPINT pnHCount, IN LPINT pnVCount)
{
    *pnHCount = m_nHorzCount;
    *pnVCount = m_nVertCount;
    return S_OK;
}

BOOL CXImageDrawImpl::IsDrawValid()
{
    if(m_xImage == NULL || m_nHorzCount == 0 || m_nVertCount == 0)
        return FALSE;

    if(IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Image))
    {
        BITMAP Bmp = {0};
        return (::GetObject(m_xImage, sizeof(Bmp), &Bmp) > 0);
    }
    else if(IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Icon))
    {
        SIZE size;
        return DrawUtil::GetXIconSize((XICON)m_xImage, &size);
    }
    return FALSE;
}

HRESULT CXImageDrawImpl::Draw(IN XCANVAS xCanvas, const RECT* lpDes)
{
    RECT rcSrc = {0};

    if(m_nHorzCount == 0 || m_nVertCount == 0)
        return E_FAIL;

    LONG nWidth = m_Size.cx / m_nHorzCount;
    LONG nHeight = m_Size.cy / m_nVertCount;

    rcSrc.left = nWidth * m_nHorzIndex;
    rcSrc.top  = nHeight * m_nVertIndex;
    rcSrc.right = rcSrc.left + nWidth;
    rcSrc.bottom = rcSrc.top + nHeight;

    return Draw(xCanvas, &rcSrc, lpDes);
}

HRESULT CXImageDrawImpl::Draw(IN XCANVAS xCanvas, IN const RECT* lpSrc, IN const RECT* lpDes)
{
    XIMAGE xImage = IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Colorize) ? m_xColorizeImage : m_xImage;
    xImage = (xImage == NULL) ? m_xImage : xImage;
    return DrawImpl(xCanvas, xImage, lpSrc, lpDes);
}

HANDLE CXImageDrawImpl::GetDrawBuffer(IN SIZE* pSize)
{
    if(IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Icon))
    {
        return BUFFER_IMAGE_STUB_HANDLE;
    }

    SIZE sizeFull = {pSize->cx * m_nHorzCount, pSize->cy * m_nVertCount};
    BufferMap::iterator IteBuffer = m_BufferMap.begin();
    for(; IteBuffer != m_BufferMap.end(); ++ IteBuffer)
    {
        stBufferInfo& info = IteBuffer->second;
        if(info.size.cx == sizeFull.cx && info.size.cy == sizeFull.cy)
        {
            ++ info.nRef;
            return (HANDLE)info.xBuffer;
        }
    }

    XInstPtr<IXImageMgr> pImageMgr;
    XASSERT_ERROR(pImageMgr != NULL, _T("GetDrawBuffer Failed."));
    if(pImageMgr == NULL)
        return NULL;

    XIMAGE xBuffer = pImageMgr->GetBufferImage(m_xImage, &sizeFull);
    stBufferInfo info = {xBuffer, 1, sizeFull};
    std::pair<BufferMap::iterator, bool> result = m_BufferMap.insert(BufferMap::value_type((HANDLE)xBuffer, info));
    if(!result.second)
    {
        pImageMgr->ReleaseBufferImage(xBuffer);
        xBuffer = NULL;
    }
    return xBuffer;
}

void CXImageDrawImpl::ReleaseDrawBuffer(IN HANDLE hBuffer)
{
    BufferMap::iterator IteBuffer = m_BufferMap.find(hBuffer);
    if(IteBuffer == m_BufferMap.end())
        return;

    -- IteBuffer->second.nRef;
    if(IteBuffer->second.nRef <= 0)
    {
        XInstPtr<IXImageMgr> pImageMgr;
        XASSERT_ERROR(pImageMgr != NULL, _T("GetDrawBuffer Failed."));
        if(pImageMgr == NULL)
            return;

        pImageMgr->ReleaseBufferImage(IteBuffer->second.xBuffer);
        m_BufferMap.erase(IteBuffer);
    }
}

HRESULT CXImageDrawImpl::DrawBuffer(IN XCANVAS xCanvas, IN HANDLE hBuffer, IN const RECT* lpDes)
{
    if(hBuffer == BUFFER_IMAGE_STUB_HANDLE)
        return Draw(xCanvas, lpDes);

    XASSERT_ERROR(!IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Colorize), _T("Buffer doesn't suppert Colorize"));

    RECT rcSrc = {0};

    if(m_nHorzCount == 0 || m_nVertCount == 0)
        return E_FAIL;
/*
#ifdef _DEBUG
    SIZE size = {0};
    XIMAGE xBuffer = (XIMAGE)hBuffer;
    XASSERT_ERROR(DrawUtil::GetXImageSize(xBuffer, &size), _T("Bad Buffer Image"));
    XASSERT_ERROR(size.cx == (lpDes->right - lpDes->left) * m_nHorzCount
        && size.cy == (lpDes->bottom - lpDes->top) * m_nVertCount,
        _T("Different Size between des and buffer"));
#endif // _DEBUG
//*/

    LONG nWidth = lpDes->right - lpDes->left;
    LONG nHeight = lpDes->bottom - lpDes->left;

    rcSrc.left = nWidth * m_nHorzIndex;
    rcSrc.top  = nHeight * m_nVertIndex;
    rcSrc.right = rcSrc.left + nWidth;
    rcSrc.bottom = rcSrc.top + nHeight;

    return DrawImpl(xCanvas, (XIMAGE)hBuffer, &rcSrc, lpDes);
}

void CXImageDrawImpl::EnableColorize(IN BOOL bEnable)
{
    m_dwFlags = IntSkinImpl::ModifyFlags(m_dwFlags, ImageFlag_Colorize, bEnable);
}

void CXImageDrawImpl::Colorize(IN int hue, IN int sat, IN int light)
{
    XInstPtr<IXImageMgr> pImageMgr;
    if(pImageMgr == NULL)
        return;

    XASSERT_ERROR(!IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Icon), _T("IXImageDraw::Colorize, ColorizeIcon Not Implement"));
    if(IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Image))
        m_xColorizeImage = pImageMgr->ColorizeImage(m_xImage, m_xColorizeImage, hue, sat, light);
}

HRESULT CXImageDrawImpl::put_AlphaMode(IN eImageAlphaMode nAlphaMode)
{
    m_nAlphaMode = nAlphaMode;
    return S_OK;
}

HRESULT CXImageDrawImpl::get_AlphaMode(OUT eImageAlphaMode* pnAlphaMode)
{
    *pnAlphaMode = m_nAlphaMode;
    return S_OK;
}

HRESULT CXImageDrawImpl::put_AlphaValue(IN BYTE byAlphaValue)
{
    m_byAlphaValue = byAlphaValue;
    return S_OK;
}

HRESULT CXImageDrawImpl::get_AlphaValue(OUT LPBYTE pbyAlphaValue)
{
    *pbyAlphaValue = m_byAlphaValue;
    return S_OK;
}

HRESULT CXImageDrawImpl::put_TransparentColor(IN COLORREF clrTrans)
{
    m_clrTrans = clrTrans;
    return S_OK;
}

HRESULT CXImageDrawImpl::get_TransparentColor(OUT LPCOLORREF pclrTrans)
{
    *pclrTrans = m_clrTrans;
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////
// IBaseObj
void CXImageDrawImpl::OnFinalRelease()
{
    superclass::OnFinalRelease();
    ReleaseData();
}

//////////////////////////////////////////////////////////////////////////
// CXImageDrawImpl
void CXImageDrawImpl::ReleaseData()
{
    if(m_xImage == NULL)
        return;

    HRESULT hResult = S_OK;
    XPtr<IXImageMgr> pImageMgr = NULL;

    hResult = Util::CreateObject(&pImageMgr);
    if(FAILED(hResult))
        return;

    BufferMap::iterator IteBuffer = m_BufferMap.begin();
    for(; IteBuffer != m_BufferMap.end(); ++ IteBuffer)
    {
        pImageMgr->ReleaseBufferImage(IteBuffer->second.xBuffer);
    }
    m_BufferMap.clear();

    if(IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Image))
    {
        pImageMgr->ReleaseImage(m_xImage);
        m_xImage = NULL;
    }
    else if(IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Icon))
    {
        if(!IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_NotDelete))
        {
            HRESULT hResult = S_OK;
            XPtr<IXIconMgr> pIconMgr = NULL;

            hResult = Util::CreateObject(&pIconMgr);
            if(FAILED(hResult))
                return;

            pIconMgr->ReleaseIcon((XICON)m_xImage);
            m_xImage = NULL;
        }
    }

    if(m_xColorizeImage != NULL)
    {
        pImageMgr->ReleaseColorizeImage(m_xColorizeImage);
        m_xColorizeImage = NULL;
    }

    memset(&m_Size, 0, sizeof(m_Size));

    m_nHorzIndex = 0;
    m_nVertIndex = 0;

    m_nHorzCount = 1;
    m_nVertCount = 1;

    m_dwFlags = IntSkinImpl::ModifyFlags(m_dwFlags, ImageFlag_NotDelete, FALSE);
}

HRESULT CXImageDrawImpl::DrawImpl(IN XCANVAS xCanvas, IN XIMAGE xImage, IN const RECT* lpSrc, IN const RECT* lpDes)
{
    if(m_xImage == NULL)
        return E_FAIL;

    if(IntSkinImpl::CheckFlags(m_dwFlags, ImageFlag_Icon))
    {
        return DrawUtil::DrawIcon(xCanvas, (XICON)m_xImage, lpDes);
    }

    HRESULT hResult = E_INVALIDARG;
    switch(m_nAlphaMode)
    {
    case Alpha_BitBlt:
        if(m_DrawType != IDT_Plain)
            hResult = DrawUtil::BitDrawEx(xCanvas, xImage, lpSrc, lpDes, &m_DrawParams, m_DrawType);
        else
            hResult = DrawUtil::BitDraw(xCanvas, xImage, lpSrc, lpDes);
        break;

    case Alpha_DefinedAlpha:
        if(m_DrawType != IDT_Plain)
            hResult = DrawUtil::DefinedAlphaDrawEx(xCanvas, xImage, lpSrc, lpDes,&m_DrawParams, m_DrawType);
        else
            hResult = DrawUtil::DefinedAlphaDraw(xCanvas, xImage, lpSrc, lpDes);
        break;

    case Alpha_AlphaValue:
        if(m_DrawType != IDT_Plain)
            hResult = DrawUtil::ValuedAlphaDrawEx(xCanvas, xImage, lpSrc, lpDes, m_byAlphaValue,&m_DrawParams, m_DrawType);
        else
            hResult = DrawUtil::ValuedAlphaDraw(xCanvas, xImage, lpSrc, lpDes, m_byAlphaValue);
        break;

    case Alpha_TransColor:
        if(m_DrawType != IDT_Plain)
            hResult = DrawUtil::TransparentDrawEx(xCanvas, xImage, lpSrc, lpDes, m_clrTrans,&m_DrawParams, m_DrawType);
        else
            hResult = DrawUtil::TransparentDraw(xCanvas, xImage, lpSrc, lpDes, m_clrTrans);
        break;

    case Alpha_TransAlpha:
        if(m_DrawType != IDT_Plain)
            hResult = DrawUtil::TransparentAlphaDrawEx(xCanvas, xImage, lpSrc, lpDes, m_byAlphaValue, m_clrTrans,&m_DrawParams, m_DrawType);
        else
            hResult = DrawUtil::TransparentAlphaDraw(xCanvas, xImage, lpSrc, lpDes, m_byAlphaValue, m_clrTrans);
        break;

    default:
        hResult = E_INVALIDARG;
        break;
    }

    XASSERT(ASSERT_LEVEL_ERROR, SUCCEEDED(hResult), _T("CXImageDrawImpl::Draw"));

    return hResult;
}